#pragma once
#include "storage/page.h"

namespace boltDB {
/*
总体上实现其实很简单就是RAII思想，以及仿照scoped_lock 还有 unique_ptr的实现
    实现的思路就是，读/写guard一旦被创建出来，
    构造函数就会自动对页面加读/写锁；被销毁时，析构函数就会自动的释放锁。
    PageGuard 也是一样的，它一旦被创建就意味这
    这个页面被引用了，就会在构造函数自动增加引用计数，
    被销毁就会释放页面，减小引用计数。
*/
class BufferPoolManager;
class ReadPageGuard;
class WritePageGuard;

class BasicPageGuard {
 public:
  BasicPageGuard() = default;

  BasicPageGuard(BufferPoolManager *bpm, Page *page) : bpm_(bpm), page_(page) {}

  BasicPageGuard(const BasicPageGuard &) = delete;
  auto operator=(const BasicPageGuard &) -> BasicPageGuard & = delete;

  BasicPageGuard(BasicPageGuard &&that) noexcept;

  void Drop();

  auto operator=(BasicPageGuard &&that) noexcept -> BasicPageGuard &;

  ~BasicPageGuard();

  auto UpgradeRead() -> ReadPageGuard;

  auto UpgradeWrite() -> WritePageGuard;

  auto PageId() -> page_id_t { return page_->GetPageId(); }

  auto GetData() -> const char * { return page_->GetData(); }

  template <class T>
  auto As() -> const T * {
    return reinterpret_cast<const T *>(GetData());
  }

  auto GetDataMut() -> char * {
    is_dirty_ = true;
    return page_->GetData();
  }

  template <class T>
  auto AsMut() -> T * {
    return reinterpret_cast<T *>(GetDataMut());
  }

 private:
  friend class ReadPageGuard;
  friend class WritePageGuard;

  [[maybe_unused]] BufferPoolManager *bpm_{nullptr};
  Page *page_{nullptr};
  bool is_dirty_{false};
};

class ReadPageGuard {
 public:
  ReadPageGuard() = default;
  ReadPageGuard(BufferPoolManager *bpm, Page *page) : guard_(bpm, page) {}
  ReadPageGuard(const ReadPageGuard &) = delete;
  auto operator=(const ReadPageGuard &) -> ReadPageGuard & = delete;

  ReadPageGuard(ReadPageGuard &&that) noexcept;

  auto operator=(ReadPageGuard &&that) noexcept -> ReadPageGuard &;

  void Drop();

  ~ReadPageGuard();

  auto PageId() -> page_id_t { return guard_.PageId(); }

  auto GetData() -> const char * { return guard_.GetData(); }

  template <class T>
  auto As() -> const T * {
    return guard_.As<T>();
  }

 private:
  BasicPageGuard guard_;
};

class WritePageGuard {
 public:
  WritePageGuard() = default;
  WritePageGuard(BufferPoolManager *bpm, Page *page) : guard_(bpm, page) {}
  WritePageGuard(const WritePageGuard &) = delete;
  auto operator=(const WritePageGuard &) -> WritePageGuard & = delete;

  WritePageGuard(WritePageGuard &&that) noexcept;

  auto operator=(WritePageGuard &&that) noexcept -> WritePageGuard &;

  void Drop();

  ~WritePageGuard();

  auto PageId() -> page_id_t { return guard_.PageId(); }

  auto GetData() -> const char * { return guard_.GetData(); }

  template <class T>
  auto As() -> const T * {
    return guard_.As<T>();
  }

  auto GetDataMut() -> char * { return guard_.GetDataMut(); }

  template <class T>
  auto AsMut() -> T * {
    return guard_.AsMut<T>();
  }

 private:
  BasicPageGuard guard_;
};
}  // namespace boltDB